﻿using System;
using System.Collections.Generic;
using System.Linq;
using Logic.Locations;
using Logic.MiniGameLogic;
using Logic.Units;

namespace Logic
{
    /// <summary>
    ///     Менеджер игры
    /// </summary>
    public sealed class ManagerGames
    {
        #region Поля

        /// <summary>
        ///     Количество ходов для перемещения
        /// </summary>
        public int Move { get; private set; }

        /// <summary>
        ///     Количество ходов для захвата
        /// </summary>
        public int Grab { get; private set; }

        /// <summary>
        ///     Новые войска полученные в начале хода
        /// </summary>
        public readonly Sector SectorNewTroops = new Sector(-1);

        /// <summary>
        ///     Войска какие будут перемещенны
        /// </summary>
        public readonly Sector UnitToMove = new Sector(-1);

        /// <summary>
        ///     Сотояние игры
        /// </summary>
        public Step Step;

        /// <summary>
        ///     Локация
        /// </summary>
        public readonly Location Location;

        /// <summary>
        ///     Мини игра
        /// </summary>
        private IMiniGame _miniGame;

        #endregion

        #region Event

        /// <summary>
        ///     Изменение информации в секторе
        /// </summary>
        public event Action<int> ElementChanged;

        private void OnElementChanged(int obj)
        {
            Action<int> handler = ElementChanged;
            if (handler != null) handler(obj);
        }

        /// <summary>
        ///     Отмена начатого хода
        /// </summary>
        public event Action Reset;

        private void OnReset()
        {
            Action handler = Reset;
            if (handler != null) handler();
        }

        /// <summary>
        ///     Изменение шага
        /// </summary>
        public event Action<Step> ChangeStep;

        private void OnChangeStep(Step step)
        {
            Action<Step> handler = ChangeStep;
            if (handler != null) handler(step);
        }

        /// <summary>
        ///     Игра законченна
        /// </summary>
        public event Action GameOver;

        private void OnGameOver()
        {
            Action handler = GameOver;
            if (handler != null) handler();
        }

        #endregion

        /// <summary>
        ///     Конструктор
        /// </summary>
        /// <param name="location">Локация</param>
        public ManagerGames(Location location)
        {
            Location = location;
            Reset += () => UnitToMove.Clear();
        }

        /// <summary>
        ///     Начало нового раунда
        /// </summary>
        private void New()
        {
            Grab = 3;
            Move = 3;
            SectorNewTroops.AddReinforcement(GetReinforcements());
        }

        /// <summary>
        ///     Перейти к следующему шагу
        /// </summary>
        public void NextStep()
        {
            switch (Step)
            {
                case Step.Distribution:
                    Step = Step.Engagement;
                    if (!Attacking())
                    {
                        Step = Step.Moving;
                    }
                    break;
                case Step.Engagement:
                    Step = Step.Moving;
                    if (!Movements())
                    {
                        New();
                        Step = Step.Distribution;
                    }
                    break;
                case Step.Moving:
                    New();
                    Step = Step.Distribution;
                    break;
            }
            OnChangeStep(Step);
        }

        /// <summary>
        ///     Получить подкрепление, состав определяется успехами игрока
        /// </summary>
        /// <returns>Подкрепление</returns>
        private IEnumerable<IUnit> GetReinforcements()
        {
            List<IUnit> result = new List<IUnit>
            {
                new Infantry(count: 3)
            };

            if (Location.Count(x => x.State == SectorState.Player) > Location.Count()*0.3)
            {
                result.Add(new ArmoredCar(count: 2));
            }

            if (Location.Count(x => x.State == SectorState.Player) > Location.Count()*0.5)
            {
                result.Add(new Tank());
            }

            return result;
        }

        /// <summary>
        ///     Распределение войск
        /// </summary>
        public void Distribution(int id)
        {
            Sector sectorTo = Location[id];
            if (sectorTo.State == SectorState.Player
                && UnitToMove.Count > 0)
            {
                sectorTo.AddReinforcement(UnitToMove);
                SectorNewTroops.RemoveReinforcement(UnitToMove);
                OnElementChanged(id);
                UnitToMove.Clear();

                if (SectorNewTroops.Count == 0)
                {
                    NextStep();
                }
            }
            else OnReset();
        }

        /// <summary>
        ///     Захват секторов
        /// </summary>
        public void Engagement(int idFrom, int idTo)
        {
            Sector sectorFrom = Location[idFrom];
            Sector sectorTo = Location[idTo];

            if ((sectorFrom.State == SectorState.Player) &&
                sectorFrom.Possibly(sectorTo) && sectorFrom.State != sectorTo.State
                && sectorFrom.Count > UnitToMove.Count && idFrom != idTo
                && UnitToMove.Count > 0)
            {
                Grab--;

                List<IUnit> tmp = UnitToMove.Select(x => ((IUnit) ((UnitTemplate) x).Clone())).ToList();

                _miniGame.SetOpponents(sectorTo.ToList(), UnitToMove.ToList());

                _miniGame.EndGame += (Action<List<IUnit>>) (winer =>
                {
                    sectorTo.Defenders = winer;
                    sectorFrom.RemoveReinforcement(tmp);
                    UnitToMove.Clear();

                    OnElementChanged(idFrom);
                    OnElementChanged(idTo);

                    if (Location.Count(x => x.State == SectorState.Enemy) == 0)
                    {
                        OnGameOver();
                    }

                    if (Attacking()) return;

                    Move += Grab;
                    NextStep();
                });
                UnitToMove.Clear();
                _miniGame.Start();
            }
            else
            {
                OnReset();
            }

            if (Grab != 0) return;
            NextStep();
        }

        /// <summary>
        ///     Проверка есть ли возможность атаковать
        /// </summary>
        /// <returns>Есть ли возможность атаковать</returns>
        private bool Attacking()
        {
            return Location.Where(x => x.State == SectorState.Player && x.Count > 1)
                .SelectMany(sector => sector.Adjacent
                    .Where(x => Location[x].State == SectorState.Enemy))
                .Any();
        }

        /// <summary>
        ///     Проверка есть ли возможность переместить отряд
        /// </summary>
        /// <returns>Есть ли возможность переместить отряд</returns>
        private bool Movements()
        {
            return Location.Where(x => x.State == SectorState.Player && x.Count > 1)
                .SelectMany(sector => sector.Adjacent
                    .Where(x => Location[x].State == SectorState.Player))
                .Any();
        }

        /// <summary>
        ///     Перемещение
        /// </summary>
        public void Moving(int idFrom, int idTo)
        {
            Sector sectorFrom = Location[idFrom];
            Sector sectorTo = Location[idTo];

            if (sectorTo.Possibly(sectorFrom) && sectorFrom.State == sectorTo.State
                && sectorFrom.Count > UnitToMove.Count && idFrom != idTo
                && UnitToMove.Count > 0)
            {
                sectorTo.AddReinforcement(UnitToMove);
                sectorFrom.RemoveReinforcement(UnitToMove);
                OnElementChanged(idFrom);
                OnElementChanged(idTo);
                UnitToMove.Clear();
                Move--;
            }
            else
            {
                OnReset();
            }

            if (Move == 0 || !Movements())
            {
                NextStep();
            }
        }

        /// <summary>
        ///     Установить мини игру
        /// </summary>
        /// <param name="miniGame">Мини игра</param>
        public void SetMiniGame(IMiniGame miniGame)
        {
            _miniGame = miniGame;
        }

        /// <summary>
        ///     Начало игры
        /// </summary>
        public void Start()
        {
            New();
            OnChangeStep(Step = Step.Distribution);
        }
    }
}